use std::collections::HashMap;

use reqwest::{
    header::{IF_MODIFIED_SINCE, REFERER},
    Url,
};
use serde::{Deserialize, Deserializer, Serialize};
use time::OffsetDateTime;
use tokio::runtime::Handle;

use super::tag::Tag;

#[allow(dead_code)]
#[derive(Copy, Clone, Debug, Hash, PartialEq, Eq)]
pub enum AbstractSize {
    SquareMedium,
    Medium,
    Large,
    Original,
    Master,
    BigThumbnail,
    SquareMediumWebp, // Так то это 360x360, но качетсво хуже чем у SquareMedium
}

#[derive(Debug, Clone, Copy, Deserialize, Serialize)]
pub struct Size {
    pub width: u32,
    pub height: u32,
}

#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Urls {
    pub square_medium: Url,
    pub medium: Url,
    // Ширина всегда 600, но если высота более чем в два раза больше ширины, то высота всегда 1200
    pub large: Url,
}

#[allow(non_snake_case)]
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct Illust {
    pub id: u32,
    pub title: String,
    #[serde(alias = "type")]
    pub work_type: String,
    pub image_urls: Urls,
    pub caption: String,
    pub restrict: u32,
    #[serde(deserialize_with = "crate::models::tag::deserialize")]
    pub tags: Vec<Tag>,
    #[serde(with = "time::serde::rfc3339")]
    pub create_date: OffsetDateTime,
    #[serde(flatten)]
    pub size: Size,
    pub page_count: u32,
    pub sanity_level: u32,
    pub x_restrict: u32,
    #[serde(deserialize_with = "Urls::deserialize")]
    pub meta_pages: Vec<Urls>,
    pub total_view: u32,
    pub total_bookmarks: u32,
    pub is_bookmarked: bool,
    pub visible: bool,
    pub is_muted: bool,
    pub illust_ai_type: u32,
}

impl Illust {
    pub fn get_meta_pages(&self) -> Vec<Illust> {
        let mut v = vec![];
        for i in &self.meta_pages {
            let mut a = self.clone();
            a.image_urls = i.clone();
            a.meta_pages.clear();
            v.push(a);
        }
        v
    }
    pub fn get_all_pages(&self) -> Vec<Illust> {
        if self.page_count == 1 {
            vec![self.clone()]
        } else {
            let mut v = vec![];
            for i in &self.meta_pages {
                let mut a = self.clone();
                a.image_urls = i.clone();
                a.meta_pages.clear();
                v.push(a);
            }
            v
        }
    }

    pub fn is_unknown(&self) -> bool {
        self.title.is_empty()
    }
}

impl Size {
    pub fn unknown() -> Size {
        Size {
            width: 0,
            height: 0,
        }
    }

    pub fn master(&self) -> Size {
        if self.width == 0 || self.height == 0 {
            return Size {
                width: 1200,
                height: 600,
            };
        }
        if self.width <= 1200 && self.height <= 1200 {
            return *self;
        }
        if self.width > self.height {
            Size {
                width: 1200,
                height: (1200.0 * self.height as f32 / self.width as f32) as u32,
            }
        } else {
            Size {
                height: 1200,
                width: (1200.0 * self.width as f32 / self.height as f32) as u32,
            }
        }
    }

    pub fn large(&self) -> Size {
        if self.width == 0 || self.height == 0 {
            return Size {
                width: 600,
                height: 600,
            };
        }
        if self.height > 2 * self.width {
            Size {
                height: 1200,
                width: (1200.0 * self.width as f32 / self.height as f32) as u32,
            }
        } else {
            Size {
                width: 600,
                height: (600.0 * self.height as f32 / self.width as f32) as u32,
            }
        }
    }

    pub fn medium(&self) -> Size {
        if self.width == 0 || self.height == 0 {
            return Size {
                width: 540,
                height: 540,
            };
        }
        if self.width > self.height {
            Size {
                width: 540,
                height: (540.0 * self.height as f32 / self.width as f32) as u32,
            }
        } else {
            Size {
                height: 540,
                width: (540.0 * self.width as f32 / self.height as f32) as u32,
            }
        }
    }
}

impl From<Size> for [usize; 2] {
    fn from(val: Size) -> Self {
        [val.width as usize, val.height as usize]
    }
}

/// If status if 200 then true. If not 200 or internet is broken then false
pub fn is_valid_url(url: Url) -> bool {
    let handle = Handle::current();
    let client = reqwest::Client::new();
    let resp = handle.block_on(
        client
            .get(url)
            .header(REFERER, "https://www.pixiv.net/")
            .header(IF_MODIFIED_SINCE, "Anything date")
            .send(),
    );
    if let Ok(resp) = resp {
        resp.status().is_success()
    } else {
        false
    }
}

impl Urls {
    // pub fn original(&self) -> Url {
    //     let s = self
    //         .square_medium
    //         .to_string()
    //         .replace("c/360x360_70/", "")
    //         .replace("img-master", "img-original")
    //         .replace("_square1200", "");
    //     let url: Url = s.parse().unwrap();
    //     let url1: Url = s.replace("jpg", "png").parse().unwrap();
    //     let url2 = s.replace("jpg", "gif").parse().unwrap();
    //     let b = reqwest::blocking::Client::new();
    //     if is_valid_url(url.clone()) {
    //         url
    //     } else if is_valid_url(url1.clone()) {
    //         url1
    //     } else {
    //         url2
    //     }
    // }

    // У master одна сторона 1200, она же наибольшая
    pub fn master(&self) -> Url {
        self.square_medium
            .to_string()
            .replace("c/360x360_70/", "")
            .replace("_square1200", "_master1200")
            .parse()
            .unwrap()
    }

    // pub fn illust_id(&self) -> String {
    //     let end = self.square_medium.find("_p").unwrap();
    //     let start = self.square_medium.rfind('/').unwrap();
    //     self.square_medium[start..=end].to_string()
    // }

    pub fn square_medium_webp(&self) -> Url {
        self.square_medium
            .to_string()
            .replacen("c/360x360_70/", "c/360x360_10_webp/", 1)
            .parse()
            .unwrap()
    }

    pub fn by_abstract_size(&self, abstract_size: AbstractSize) -> Url {
        match abstract_size {
            AbstractSize::SquareMedium => self.square_medium.clone(),
            AbstractSize::Medium => self.medium.clone(),
            AbstractSize::Large => self.large.clone(),
            // AbstractSize::Original => self.original(),
            AbstractSize::Master => self.master(),
            AbstractSize::BigThumbnail => self.square_medium.clone(),
            AbstractSize::SquareMediumWebp => self.square_medium_webp(),
            AbstractSize::Original => todo!(),
        }
    }

    pub fn deserialize<'de, D>(d: D) -> Result<Vec<Urls>, D::Error>
    where
        D: Deserializer<'de>,
    {
        #[derive(Deserialize)]
        #[serde(untagged)]
        enum Variants {
            One(Vec<HashMap<String, Urls>>),
            Two(Vec<Urls>),
        }
        let s = <Variants>::deserialize(d)?;
        match s {
            Variants::One(a) => Ok(a
                .into_iter()
                .map(|x| x.into_values().next().unwrap())
                .collect()),
            Variants::Two(a) => Ok(a),
        }
    }
}
